package com.nudge.portlet;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * This class handle the mapping of measure data into a NudgeData object.
 *
 * @author Frederic Massart
 */
public class MapNudgeData {

    private final static Logger LOG = LoggerFactory.getLogger(MapNudgeData.class);


    private final static SimpleDateFormat nudgeFormatDate = new SimpleDateFormat("yyyy-MM-dd HH:mm");


    /**
     * Map the 'collectingDate' data in the nudgeData object.
     *
     * @param collectingDate the data to map
     * @param nudgeData      the target object to complete
     */
    protected void mapCollectingDate(final String collectingDate, final NudgeData nudgeData) throws ParseException {

        if (StringUtils.isBlank(collectingDate)) {
            return;
        }

        Date date;
        try {
            date = nudgeFormatDate.parse(collectingDate);
        } catch (ParseException pe) {
            LOG.error("Parsing collectingDate failed, incorrect value '{}' for a date", collectingDate);
            throw pe;
        }
        nudgeData.setCollectingDate(date);
    }


    /**
     * Map the 'count' data in the nudgeData object.
     *
     * @param count     the data to map
     * @param nudgeData the target object to complete
     */
    protected void mapCount(final String count, final NudgeData nudgeData) {

        if (StringUtils.isBlank(count)) {
            return;
        }

        try {
            nudgeData.setCount(Long.decode(count));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing count failed, incorrect value '{}' for a long", count);
            throw nfe;
        }
    }

    /**
     * Map the 'avgResponseTime' data in the nudgeData object.
     *
     * @param avgResponseTime the data to map
     * @param nudgeData       the target object to complete
     */
    protected void mapAvgResponseTime(final String avgResponseTime, final NudgeData nudgeData) {

        if (StringUtils.isBlank(avgResponseTime)) {
            return;
        }

        try {
            nudgeData.setAvgResponseTime(Double.parseDouble(avgResponseTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing avgResponseTime failed, incorrect value '{}' for a double", avgResponseTime);
            throw nfe;
        }
    }

    /**
     * Map the 'maxResponseTime' data in the nudgeData object.
     *
     * @param maxResponseTime the data to map
     * @param nudgeData       the target object to complete
     */
    protected void mapMaxResponseTime(final String maxResponseTime, final NudgeData nudgeData) {

        if (StringUtils.isBlank(maxResponseTime)) {
            return;
        }

        try {
            nudgeData.setMaxResponseTime(Double.parseDouble(maxResponseTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing maxResponseTime failed, incorrect value '{}' for a double", maxResponseTime);
            throw nfe;
        }
    }

    /**
     * Map the 'minResponseTime' data  in the nudgeData object.
     *
     * @param minResponseTime the data to map
     * @param nudgeData       the target object to complete
     */
    protected void mapMinResponseTime(final String minResponseTime, final NudgeData nudgeData) {

        if (StringUtils.isBlank(minResponseTime)) {
            return;
        }

        try {
            nudgeData.setMinResponseTime(Double.parseDouble(minResponseTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing minResponseTime failed, incorrect value '{}' for a double", minResponseTime);
            throw nfe;
        }
    }

    /**
     * Map the 'totalTime' data in the nudgeData object.
     *
     * @param totalTime the data to map
     * @param nudgeData the target object to complete
     */
    protected void mapTotalTime(final String totalTime, final NudgeData nudgeData) {

        if (StringUtils.isBlank(totalTime)) {
            return;
        }

        try {
            nudgeData.setTotalTime(Double.parseDouble(totalTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing totalTime failed, incorrect value '{}' for a double", totalTime);
            throw nfe;
        }
    }

    /**
     * Map the 'avgDBResponseTime' data in the nudgeData object.
     *
     * @param avgDBResponseTime the data to map
     * @param nudgeData         the target object to complete
     */
    protected void mapAvgDBResponseTime(final String avgDBResponseTime, final NudgeData nudgeData) {

        try {
            nudgeData.setAvgDBResponseTime(Double.parseDouble(avgDBResponseTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing avgDBResponseTime failed, incorrect value '{}' for a double", avgDBResponseTime);
            throw nfe;
        }
    }

    /**
     * Map the 'maxDBResponseTime' data in the nudgeData object.
     *
     * @param maxDBResponseTime the data to map
     * @param nudgeData         the target object to complete
     */
    protected void mapMaxDBResponseTime(final String maxDBResponseTime, final NudgeData nudgeData) {

        try {
            nudgeData.setMaxDBResponseTime(Double.parseDouble(maxDBResponseTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing maxDBResponseTime failed, incorrect value '{}' for a double", maxDBResponseTime);
            throw nfe;
        }
    }

    /**
     * Map the 'minDBResponseTime' data in the nudgeData object.
     *
     * @param minDBResponseTime the data to map
     * @param nudgeData         the target object to complete
     */
    protected void mapMinDBResponseTime(final String minDBResponseTime, final NudgeData nudgeData) {

        try {
            nudgeData.setMinDBResponseTime(Double.parseDouble(minDBResponseTime));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing minDBResponseTime failed, incorrect value '{}' for a double", minDBResponseTime);
            throw nfe;
        }
    }

    /**
     * Map the 'errors' data in the nudgeData object.
     *
     * @param errors    the data to map
     * @param nudgeData the target object to complete
     */
    protected void mapErrors(final String errors, final NudgeData nudgeData) {

        try {
            nudgeData.setErrors(Integer.parseInt(errors));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing the errors data failed, incorrect value '{}' for a integer", errors);
            throw nfe;
        }
    }

    /**
     * Map the 'satisfyingClicks' data in the nudgeData object.
     *
     * @param satisfyingClicks the data to map
     * @param nudgeData        the target object to complete
     */
    public void mapSatisfyingClicks(String satisfyingClicks, NudgeData nudgeData) {

        if (StringUtils.isBlank(satisfyingClicks)) {
            return;
        }

        try {
            nudgeData.setSatisfyingClicks(Long.decode(satisfyingClicks));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing satisfyingClicks failed, incorrect value '{}' for a long", satisfyingClicks);
            throw nfe;
        }
    }

    /**
     * Map the 'tolerableClicks' data in the nudgeData object.
     *
     * @param tolerableClicks the data to map
     * @param nudgeData       the target object to complete
     */
    public void mapTolerableClicks(String tolerableClicks, NudgeData nudgeData) {

        if (StringUtils.isBlank(tolerableClicks)) {
            return;
        }

        try {
            nudgeData.setTolerableClicks(Long.decode(tolerableClicks));
        } catch (NumberFormatException nfe) {
            LOG.error("Parsing tolerableClicks failed, incorrect value '{}' for a long", tolerableClicks);
            throw nfe;
        }
    }


    /**
     * Create a NudgeData object from a CSV string coming from a nudge server response.
     *
     * @param dataUnparsed the data to parse and map, this string must respect 11 elements separated by a semicolon.
     *                     The order of elements is : <br />
     *                     0- collecting Date/time <br />
     *                     1- Count (represents the users clicks) <br />
     *                     2- Average response time <br />
     *                     3- Max response time <br />
     *                     4- Min response time <br />
     *                     5- Total time <br />
     *                     6- Average DB response time <br />
     *                     7- Max DB response time <br />
     *                     8- Min DB response time <br />
     *                     9- Errors <br />
     *                     10- Satisfying clicks <br />
     *                     11- Tolerable clicks
     * @return the NudgeData object created
     */
    public NudgeData createNudgeData(final String dataUnparsed) {

        NudgeData nudgeData = new NudgeData();

        if (StringUtils.isEmpty(dataUnparsed)) {
            return nudgeData;
        }


        String[] data = StringUtils.split(dataUnparsed, ";");

        // TODO create a method to validate the structure of the String param
        if (data.length != 12) {
            LOG.error("The data to parse in order to create a NudgeData object, does not respect the expected format");
        } else {
            // make all mappings
            try {
                mapCollectingDate(data[0], nudgeData);
            } catch (ParseException pe) {
                LOG.error("The collectingDate is incorrect, the creation of a NudgeData object will not be complete", pe);
            }
            mapCount(data[1], nudgeData);
            mapAvgResponseTime(data[2], nudgeData);
            mapMaxResponseTime(data[3], nudgeData);
            mapMinResponseTime(data[4], nudgeData);
            mapTotalTime(data[5], nudgeData);
            mapAvgDBResponseTime(data[6], nudgeData);
            mapMaxDBResponseTime(data[7], nudgeData);
            mapMinDBResponseTime(data[8], nudgeData);
            mapErrors(data[9], nudgeData);
            mapSatisfyingClicks(data[10], nudgeData);
            mapTolerableClicks(data[11], nudgeData);
        }

        return nudgeData;
    }


    /**
     * Getter of the date format used in the nudge server response.
     *
     * @return the format of the date
     */
    public SimpleDateFormat getNudgeFormatDate() {
        return nudgeFormatDate;
    }

}
